<template>
  <view>
    <view class="content" @touchstart="hideDrawer">
      <scroll-view class="msg-list" scroll-y="true" :scroll-with-animation="scrollAnimation" :scroll-top="scrollTop"
                   :scroll-into-view="scrollToView" @scrolltoupper="loadHistory" upper-threshold="50">
        <view class="row" v-for="(row,index) in msgList" :key="index" :id="'msg' + row.id">
          <!-- 系统消息 -->
          <block v-if="row.type === 5">
            <view class="system">
              <!-- 文字消息 -->
              <view class="text">
                {{ row.content }}
              </view>
            </view>
          </block>
          <!-- 用户消息 -->
          <block>
            <!-- 自己发出的消息 -->
            <view class="my" v-if="row.senderId === id">
              <!-- 左-消息 -->
              <view class="left">
                <!-- 文字消息 -->
                <view v-if="row.type === TEXT_TYPE" class="bubble">
                  <view>{{ row.content }}</view>
                </view>
                <!-- 文字和表情消息 -->
                <view v-if="row.type === HTML_TYPE" class="bubble">
                  <u-parse :html="prefix + row.content + suffix" :selectable="true" :tag-style="style"/>
                </view>
                <!-- 语音消息 -->
                <view v-if="row.type === AUDIO_TYPE" class="bubble voice" @tap="playVoice(row)" :class=" playMsgid == row.id ? 'play' : '' ">
                  <view class="length">{{ row.extra.length }}</view>
                  <view class="icon my-voice"></view>
                </view>
                <!-- 图片消息 -->
                <view v-if="row.type === IMG_TYPE" class="bubble img" @tap="showPic(row)">
                  <image :src="bucketUrl + row.content" :style=" { 'width': row.extra.w + 'px' , 'height' : row.extra.h + 'px' } "></image>
                </view>

              </view>
              <!-- 右-头像 -->
              <view class="right">
                <avatar-group :avatar="avatar" :text="name"/>
              </view>
            </view>
            <!-- 别人发出的消息 -->
            <view class="other" v-if="row.senderId !== id">
              <!-- 左-头像 -->
              <view class="left">
                <avatar-group :avatar="row.avatar" :text="row.name"/>
              </view>
              <!-- 右-用户名称-时间-消息 -->
              <view class="right">
                <view class="username">
                  <view class="time">{{ row.gmtCreate | dateFormat }}</view>
                </view>
                <!-- 文字消息 -->
                <view v-if="row.type === TEXT_TYPE" class="bubble">
                  <view>{{ row.content }}</view>
                </view>
                <!-- 文字和表情消息 -->
                <view v-if="row.type === HTML_TYPE" class="bubble">
                  <u-parse :html="prefix + row.content + suffix" :selectable="true" :tag-style="style"/>
                </view>
                <!-- 语音消息 -->
                <view v-if="row.type === AUDIO_TYPE" class="bubble voice" @tap="playVoice(row)" :class=" playMsgid == row.id ? 'play' : '' ">
                  <view class="icon other-voice"></view>
                  <view class="length">{{ row.extra.length }}</view>
                </view>
                <!-- 图片消息 -->
                <view v-if="row.type === IMG_TYPE" class="bubble img" @tap="showPic(row)">
                  <image :src="bucketUrl + row.content" :style=" { 'width': row.extra.w + 'px' , 'height' : row.extra.h + 'px' } "/>
                </view>
              </view>
            </view>
          </block>
        </view>
      </scroll-view>
    </view>
    <!-- 抽屉栏 -->
    <view class="popup-layer" :class="popupLayerClass" @touchmove.stop.prevent="discard">
      <!-- 表情 -->
      <swiper class="emoji-swiper" :class="{ hidden: hideEmoji }" indicator-dots="true" duration="150">
        <swiper-item v-for="(page,pid) in emojiList" :key="pid">
          <view v-for="(em,eid) in page" :key="eid" @tap="addEmoji(em)">
            <image mode="widthFix" :src="'/static/emoji/' + em.url"></image>
          </view>
        </swiper-item>
      </swiper>
      <!-- 更多功能 相册-拍照 -->
      <view class="more-layer" :class="{hidden:hideMore}">
        <view class="list">
          <view class="box">
            <u-upload @on-choose-complete="onChooseComplete" @on-success="onSuccess" :header="header" :max-count="50" :show-upload-list="false" :custom-btn="true" :source-type="chooseImage"  :action="action" :file-list="fileList" >
              <view slot="addBtn" class="slot-btn" hover-class="slot-btn__hover" hover-stay-time="150">
                <view class="icon tupian2"></view>
              </view>
            </u-upload>
          </view>
          <view class="box" @tap="camera">
            <u-upload @on-choose-complete="onChooseComplete" @on-success="onSuccess" :header="header" :max-count="1" :show-upload-list="false" :custom-btn="true"  :source-type="camera" :action="action" :file-list="fileList" >
              <view slot="addBtn" class="slot-btn" hover-class="slot-btn__hover" hover-stay-time="150">
                <view class="icon paizhao"></view>
              </view>
            </u-upload>
          </view>

        </view>
      </view>
    </view>
    <!-- 底部输入栏 -->
    <view class="input-box" :style="{bottom: b + 'px'}" :class="popupLayerClass" @touchmove.stop.prevent="discard">
      <!-- H5下不能录音，输入栏布局改动一下 -->
      <!-- #ifndef H5 -->
      <view class="voice">
        <view class="icon" :class="isVoice?'jianpan':'yuyin'" @tap="switchVoice"></view>
      </view>
      <!-- #endif -->
      <!-- #ifdef H5 -->
      <view class="more" @tap="showMore">
        <view class="icon add"></view>
      </view>
      <!-- #endif -->
      <view class="textbox">
        <view class="voice-mode" :class="[isVoice?'':'hidden',recording?'recording':'']" @touchstart="voiceBegin"
              @touchmove.stop.prevent="voiceIng" @touchend="voiceEnd" @touchcancel="voiceCancel">{{voiceTis}}</view>
        <view class="text-mode" :class="isVoice?'hidden':''">
          <view class="box">
            <textarea adjust-position="false" fixed="true" auto-height="true" v-model="textMsg" @focus="textareaFocus" @blur="textareaBlur"/>
          </view>
          <view class="em" @tap="chooseEmoji">
            <view class="icon biaoqing"></view>
          </view>
        </view>
      </view>
      <!-- #ifndef H5 -->
      <view class="more" @tap="showMore">
        <view class="icon add"></view>
      </view>
      <!-- #endif -->
      <view class="send" :class="isVoice?'hidden':''" @tap="sendText">
        <view class="btn">发送</view>
      </view>
    </view>
    <!-- 录音UI效果 -->
    <view class="record" :class="recording?'':'hidden'">
      <view class="ing" :class="willStop?'hidden':''"><view class="icon luyin2" ></view></view>
      <view class="cancel" :class="willStop?'':'hidden'"><view class="icon chehui" ></view></view>
      <view class="tis" :class="willStop?'change':''">{{recordTis}}</view>
    </view>
  </view>
</template>
<script>
import {getToken} from '@/common/auth.js';
import avatarGroup from "@/components/avatar_group/avatar_group.vue"
import { getEmojiList, getUploadUrl, getEmojiBaseUrl } from "@/utils/constants";
import { isHtml } from "@/utils/utils";
import { mapGetters } from 'vuex'
import { ChatMsgVo, TEXT_TYPE, AUDIO_TYPE, IMG_TYPE, HTML_TYPE, HTML_FORMAT } from "@/common/xiaofei-im/im_pojo"
import { MESSAGE } from "@/common/xiaofei-im/xiaofei-im-utils"

export default {
  components: {
    avatarGroup
  },
  data() {
    return {
      TEXT_TYPE, AUDIO_TYPE, IMG_TYPE, HTML_TYPE, HTML_FORMAT
      ,
      style: {
        
      },
      prefix: '<div style="display: flex;flex-wrap: wrap;align-items: center;word-wrap:break-word;">',
      suffix: '</div>',
      b: 0,
      //文字消息
      textMsg:'',
      //消息列表
      isHistoryLoading:false,
      scrollAnimation:false,
      scrollTop:0,
      scrollToView:'',
      msgList:[],
      msgImgList:[],
      // 本地缓存20条
      pageNum: 0,
      total: 20,
      pages: 0,
      //图片上传地址
      action: getUploadUrl(),
      fileList: [],
      header: {
        "Authorization": getToken()
      },
      bucketUrl: "https://xiaofei-classroom.oss-cn-hangzhou.aliyuncs.com/",
      // 从相册中选
      chooseImage: ['album'],
      // 拍照
      camera: ['camera'],
      //录音相关参数
      // #ifndef H5
      //H5不能录音
      RECORDER:uni.getRecorderManager(),
      // #endif
      isVoice:false,
      voiceTis:'按住 说话',
      recordTis:"手指上滑 取消发送",
      recording:false,
      willStop:false,
      initPoint:{identifier:0,Y:0},
      recordTimer:null,
      recordLength: 0,

      //播放语音相关参数
      AUDIO:uni.createInnerAudioContext(),
      playMsgid:null,
      VoiceTimer:null,
      // 抽屉参数
      popupLayerClass:'',
      // more参数
      hideMore:true,
      //表情定义
      hideEmoji:true,
      emojiList: getEmojiList(),
      // 群ID
      groupId: '',
    };
  },
  computed: {
    ...mapGetters([
      'id',
      'avatar',
      'name'
    ])
  },
  async onLoad(option) {
    this.groupId = option.id;
    uni.$emit("setVuexUserInfo",{user:null,isLoginPage: false});
    // 设置页面标题
    uni.setNavigationBarTitle({
      title:  option.name
    });
    // 加载历史消息
    await this.loadHistory();
    // 滚动到底部
    this.$nextTick(() => {
      //进入页面滚动到底部
      this.scrollTop = 9999;
      this.$nextTick(() => {
        this.scrollAnimation = true;
      });
    });
    //语音自然播放结束
    this.AUDIO.onEnded((res)=>{
      this.playMsgid=null;
    });
    // #ifndef H5
    //录音开始事件
    this.RECORDER.onStart((e)=>{
      this.recordBegin(e);
    })
    //录音结束事件
    this.RECORDER.onStop((e)=>{
      this.recordEnd(e);
    })
    // #endif
    uni.$on("receiveGroupMsg",this.receiveGroupMsg);
  },
  onUnload(){
    //移除监听
    uni.$off("receiveGroupMsg");
  },
  onShow(){
    this.scrollTop = 9999999;
  },
  methods:{
    async getHistoryMsg(){
      const res = await this.$u.api.queryGroupMsg(this.groupId,{pageNum: this.pageNum, pageSize: 20});
      const data = res.data;
      for (const index in data) {
        const msgItem = data[index];
        if(msgItem.type === IMG_TYPE || msgItem.type === AUDIO_TYPE){
          if(msgItem.format === HTML_FORMAT && !this.$u.test.isEmpty(msgItem.extra)){
            data[index].extra = JSON.parse(msgItem.extra);
          }
          if(msgItem.type === IMG_TYPE){
            this.msgImgList.unshift(this.bucketUrl + msgItem.content);
          }
        }
      }
      if(this.pageNum === 1){
        this.msgList = data;
      }else {
        this.msgList.unshift(...data);
      }
      this.total = res.total;
      this.pages = res.pages;
      
    },
    // 接受消息(筛选处理)
    screenMsg(msg){
      //从长连接处转发给这个方法，进行筛选处理
      if(msg.type === IMG_TYPE){
        msg = this.setPicSize(msg);
        this.msgImgList.push(msg.content);
      }
      this.msgList.push(msg);
      //非自己的消息震动
      if(msg.senderId !== this.id){
        uni.vibrateLong({
          success: (res) => {

          }
        });
      }
      this.$nextTick(() => {
        // 滚动到底
        this.scrollToView = 'msg' + msg.id
      });
    },

    //触发滑动到顶部(加载历史信息记录)
    async loadHistory(e){
      if(this.isHistoryLoading){
        return ;
      }
      //参数作为进入请求标识，防止重复请求
      this.isHistoryLoading = true;
      //关闭滑动动画
      this.scrollAnimation = false;
      //记住第一个信息ID
      let viewId = this.msgList == null || this.msgList.length === 0 ? 0 : this.msgList[0].id;
      this.pageNum = this.pageNum + 1;
      if(this.pageNum === 1 || this.pageNum < this.pages) {
         // 加载初始页面消息
        await this.getHistoryMsg();
        //这段代码很重要，不然每次加载历史数据都会跳到顶部
        if(viewId !== 0){
          this.$nextTick( () =>  {
            //跳转上次的第一行信息位置
            this.scrollToView = 'msg' +  viewId;
            this.$nextTick( () =>  {
              //恢复滚动动画
              this.scrollAnimation = true;
            });
          });
        }
      }else {
        this.pageNum = this.pageNum - 1;
      }
      this.isHistoryLoading = false;
    },
    //处理图片尺寸，如果不处理宽高，新进入页面加载图片时候会闪
    setPicSize(imgMsg){
      // 让图片最长边等于设置的最大长度，短边等比例缩小，图片控件真实改变，区别于aspectFit方式。
      //350是定义消息图片最大宽度
      let maxW = uni.upx2px(350);
      //350是定义消息图片最大高度
      let maxH = uni.upx2px(350);
      if(imgMsg.extra.w > maxW || imgMsg.extra.h > maxH){
        let scale = imgMsg.extra.w / imgMsg.extra.h;
        imgMsg.extra.w = scale > 1 ? maxW : maxH * scale;
        imgMsg.extra.h = scale > 1 ? maxW / scale : maxH;
      }
      return imgMsg;
    },
    //更多功能(点击+弹出)
    showMore(){
      this.isVoice = false;
      this.hideEmoji = true;
      if(this.hideMore){
        this.hideMore = false;
        this.openDrawer();
      }else{
        this.hideDrawer();
      }
    },
    // 打开抽屉
    openDrawer(){
      this.popupLayerClass = 'showLayer';
    },
    // 隐藏抽屉
    hideDrawer(){
      this.popupLayerClass = '';
      setTimeout(()=>{
        this.hideMore = true;
        this.hideEmoji = true;
      },150);
    },
    // 图片上传成功是调用
    onSuccess(data, index, lists, name) {
      uni.getImageInfo({
        src: lists[index].url,
        success: (image) => {
          this.send({content: data.data.fullPath, type: IMG_TYPE}, {w: image.width , h: image.height});
        },
        fail: (error) => {
          console.log(error)
        }
      });
    },
    // 选择图片完成是调用
    onChooseComplete(lists, name){
      this.hideDrawer();
    },
    // 选择表情
    chooseEmoji(){
      this.hideMore = true;
      if(this.hideEmoji){
        this.hideEmoji = false;
        this.openDrawer();
      }else{
        this.hideDrawer();
      }
    },
    //添加表情
    addEmoji(em){
      this.textMsg += em.alt;
    },

    //获取焦点，如果不是选表情ing,则关闭抽屉
    textareaFocus(){
      this.b = 13;
      if(this.popupLayerClass === 'showLayer' && this.hideMore === false){
        this.hideDrawer();
      }
    },
    textareaBlur(){
      this.b = 0;
    },
    // 发送文字消息
    sendText(){
      this.hideDrawer();//隐藏抽屉
      if(this.$u.test.isEmpty(this.textMsg)){
        return;
      }
      const content = this.replaceEmoji(this.textMsg);
      this.send({content: content, type: isHtml(content) ? HTML_TYPE: TEXT_TYPE });
      this.textMsg = '';//清空输入框
    },
    //替换表情符号为图片
    replaceEmoji(str){
      return str.replace(/\[([^(\]|\[)]*)\]/g,(item, index) => {
        for(let i=0;i<this.emojiList.length;i++){
          let row = this.emojiList[i];
          for(let j=0;j<row.length;j++){
            let EM = row[j];
            if(EM.alt === item){
              return '<img src="' + getEmojiBaseUrl() + EM.url + '" alt="' + item + '">';
            }
          }
        }
      });
    },
    // 接收消息
    receiveGroupMsg(msg){
      msg.gmtCreate = new Date(parseInt(msg.gmtcreate));
      msg.groupId = msg.receiverid;
      msg.senderId = msg.senderid;
      msg.type = msg.msgtype;
      const obj = JSON.parse(msg.friendrelationid);
      msg.avatar = obj.avatar;
      msg.name = obj.name;
      msg.extra = JSON.parse(msg.extra);
      console.log(msg);
      if (msg.groupId === this.groupId){
        this.screenMsg(msg);
      }
    },
    // 发送消息
    send({ content, type} , extra){
      // 生成唯一ID
      this.$u.api.getId().then(res => {
        const chatMsgVo = new ChatMsgVo(res.data, JSON.stringify({avatar: this.avatar, name: this.name}), this.id, this.groupId, type, content, JSON.stringify(extra),  0, HTML_FORMAT, new Date().getTime() + "");
        // 发送消息
        this.$fim.sendMsg(chatMsgVo.toGroupTransportMsgPb(), MESSAGE)
        chatMsgVo.avatar = this.avatar;
        chatMsgVo.name = this.name;
        chatMsgVo.extra = extra;
        this.screenMsg(chatMsgVo);
      }).catch(err => {
        console.log(err);
        this.$u.toast("发送失败，请重试！");
      });
    },
    // 预览图片
    showPic(msg){
      uni.previewImage({
        indicator:"none",
        current:this.bucketUrl + msg.content,
        urls: this.msgImgList
      });
    },
    // 播放语音
    playVoice(msg){
      this.playMsgid=msg.id;
      this.AUDIO.src = this.bucketUrl + msg.content;
      this.$nextTick(function() {
        this.AUDIO.play();
      });
    },
    // 录音开始
    voiceBegin(e){
      if(e.touches.length > 1){
        return ;
      }
      this.initPoint.Y = e.touches[0].clientY;
      this.initPoint.identifier = e.touches[0].identifier;
      this.RECORDER.start({format:"mp3"});//录音开始,
    },
    //录音开始UI效果
    recordBegin(e){
      this.recording = true;
      this.voiceTis='松开 结束';
      this.recordLength = 0;
      this.recordTimer = setInterval(()=>{
        this.recordLength++;
      },1000)
    },
    // 录音被打断
    voiceCancel(){
      this.recording = false;
      this.voiceTis='按住 说话';
      this.recordTis = '手指上滑 取消发送'
      this.willStop = true;//不发送录音
      this.RECORDER.stop();//录音结束
    },
    // 录音中(判断是否触发上滑取消发送)
    voiceIng(e){
      if(!this.recording){
        return;
      }
      let touche = e.touches[0];
      //上滑一个导航栏的高度触发上滑取消发送
      if(this.initPoint.Y - touche.clientY>=uni.upx2px(100)){
        this.willStop = true;
        this.recordTis = '松开手指 取消发送'
      }else{
        this.willStop = false;
        this.recordTis = '手指上滑 取消发送'
      }
    },
    // 结束录音
    voiceEnd(e){
      if(!this.recording){
        return;
      }
      this.recording = false;
      this.voiceTis='按住 说话';
      this.recordTis = '手指上滑 取消发送'
      this.RECORDER.stop();//录音结束
    },
    //录音结束(回调文件)
    recordEnd(e){
      clearInterval(this.recordTimer);
      if(!this.willStop){
        console.log("e: " + JSON.stringify(e));
        uni.uploadFile({
          url: this.action,
          name: "file",
          filePath:e.tempFilePath,
          header:this.header,
          success: res => {
            // 判断是否json字符串，将其转为json格式
            let data = this.$u.test.jsonString(res.data) ? JSON.parse(res.data) : res.data;
            if (![200, 201, 204].includes(data.code)) {
              this.$u.toast("发送失败")
            } else {
              // 上传成功
              let min = Math.trunc(this.recordLength / 60);
              let sec = this.recordLength % 60;
              min = min < 10 ? '0' + min : min;
              sec = sec < 10 ? '0' + sec : sec;
              const length = min + ':' + sec;
              this.send({content: data.data.fullPath, type: AUDIO_TYPE}, {length: length });
            }
          },
          fail: err => {
            this.$u.toast("发送失败")
          }
        })
      }else{
        console.log('取消发送录音');
      }
      this.willStop = false;
    },
    // 切换语音/文字输入
    switchVoice(){
      this.hideDrawer();
      this.isVoice = !this.isVoice;
    },
    discard(){
      return;
    }
  }
}
</script>
<style lang="scss">
@import "@/static/css/chat";
</style>
